#include <unistd.h>
#include "Channel.h"
#include "EventLoop.h"
#include "SelectDispatcher.h"

int32_t SelectDispatcher::setFdSet()
{
    if (m_channel->getSocket() >= m_maxNode)
    {
        return -1;
    }
    if (m_channel->getEvent() & static_cast<int>(FDEvent::ReadEvent))
    {
        FD_SET(m_channel->getSocket(), &m_readfds);
    }
    if (m_channel->getEvent() & static_cast<int>(FDEvent::WriteEvent))
    {
        FD_SET(m_channel->getSocket(), &m_writefds);
    }
    m_maxFd = (m_maxFd > m_channel->getSocket()) ? m_maxFd : m_channel->getSocket();
    return 0;
}

int32_t SelectDispatcher::clearFdSet()
{   
    if (m_channel->getSocket() >= m_maxNode)
    {
        return -1;
    }
    if (m_channel->getEvent() & static_cast<int>(FDEvent::ReadEvent))
    {
        FD_CLR(m_channel->getSocket(), &m_readfds);
    }
    if (m_channel->getEvent() & static_cast<int>(FDEvent::WriteEvent))
    {
        FD_CLR(m_channel->getSocket(), &m_writefds);
    }

    if (m_channel->getSocket() == m_maxFd)
    {
        int32_t newMax = -1;
        for (int32_t i = 0; i < m_maxFd; i++)
        {
            if (FD_ISSET(i, &m_readfds) || FD_ISSET(i, &m_writefds))
            {
                newMax = i;
            }
        }
        m_maxFd = (newMax == -1) ? 0 : newMax;
    }
    return 0;
}

SelectDispatcher::SelectDispatcher(EventLoop *evLoop) : Dispatcher(evLoop)
{
    FD_ZERO(&m_readfds);
    FD_ZERO(&m_writefds);
    m_maxFd = 0;
    m_name = "Select";
}

SelectDispatcher::~SelectDispatcher()
{
}
int32_t SelectDispatcher::add()
{   
    if (setFdSet() == -1)
    {
        return -1;
    }
    return 0;
}

int32_t SelectDispatcher::remove()
{
    if (clearFdSet() == -1)
    {
        return -1;
    }
    return 0;
}

int32_t SelectDispatcher::modify()
{   
    if (clearFdSet() == -1)
    {
        return -1;
    }
    if (setFdSet() == -1)
    {
        return -1;
    }
    // 通过channel释放对于的tcpConnection资源
    m_channel->destroyCallback(const_cast<void *>(m_channel->getArg()));
    return 0;
}

int32_t SelectDispatcher::dispatch(int timeout)
{
    struct timeval val;
    val.tv_sec = timeout;
    val.tv_usec = 0;
    fd_set rdtmp = m_readfds;
    fd_set wrtmp = m_writefds;
    int32_t count = select(m_maxFd + 1, &rdtmp, &wrtmp, NULL, &val);
    if (count == -1)
    {
        perror("select error");
    }
    if(count == 0)
    {
        return 0;
    }
    for (int32_t i = 0; i <= m_maxFd; i++)
    {   
        if (FD_ISSET(i, &rdtmp))
        {   
            m_evLoop->eventActive(i, (int)FDEvent::ReadEvent);
        }
        if (FD_ISSET(i, &wrtmp))
        {   
            m_evLoop->eventActive(i, (int)FDEvent::WriteEvent);
        }
    }
    return 0;
}